﻿//
// Created by Administrator on 2022/6/17.
//
#define WIN32_LEAN_AND_MEAN

//

#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include <wx/app.h>
#include <wx/cmdline.h>
#include "wx/intl.h"

#include <cstdio>
#include <Iphlpapi.h>
#include <winsock2.h>
#include <cstring>
#include <string>
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/locale.hpp>
#include <windows.h>
#include <codecvt>
#include <versionhelpers.h>
#include "action_event.h"
#include "ids.h"

#include "public/tag.h"
#include "public/conf.h"
#include "public/windows/utils.h"
#include "WorkThread.h"

#define BUFF_LEN 65536

using namespace std;
using namespace rapidjson;
using namespace boost::locale::conv;

extern int touch_server_port;
extern string touch_server_ip;
extern bool backup;
extern int delay;
extern bool loopIt;
extern int server_status_delay;

char recvBuf[BUFF_LEN];

/*
 * client
 * server
 */
StringBuffer GetServerStatus(){
    Document document;
    document.SetObject();
    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();

    document.AddMember( key_request_get,
            StringRef( key_request_get_value_server_status ), allocator );

    StringBuffer stringBuffer;
    Writer<StringBuffer> writer( stringBuffer );
    document.Accept(writer );

    return stringBuffer;
}

//https://www.codenong.com/6140223/
//https://www.boost.org/doc/libs/1_78_0/libs/locale/doc/html/charset_handling.html
//两个关键字: post/get
//post: tag: host_info
//get: nullptr
//post: tag: messages
//get: tag: messages
StringBuffer GetResponseDocument( ){
    Document document;
    document.SetObject();
    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();

    //wxString _host_name( L"我们" );
    //_host_name.sprintf("%s", host_name );
    //utf8 ok
    //string s = u8"DEVELOPER";

    //Value& v = document["host_name"];
    //string _s = v.GetString();

    IP_ADAPTER_INFO  *pAdapterInfo;
    ULONG            ulOutBufLen;
    DWORD            dwRetVal;

    pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
    ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS) {
        free (pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO *) malloc ( ulOutBufLen );
    }
    if ((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) != ERROR_SUCCESS) {
        printf("GetAdaptersInfo call failed with %lu\n", dwRetVal);
    }
    PIP_ADAPTER_INFO pAdapter = pAdapterInfo;

    //WCHAR _mac[64];
    char _mac[64];
    //char device_name[128+4], ip[4*4];
    memset( _mac, 0, 64 );
    //
    Value interfaces(rapidjson::kArrayType );
    while (pAdapter) {
        Value interface(rapidjson::kObjectType );
        //strcpy( device_name, pAdapter->Description );
        Value v_device_name( rapidjson::kStringType );
        v_device_name.SetString( pAdapter->Description, allocator ) ;
        interface.AddMember("interface_name", v_device_name, allocator );
        printf( "%s\n", pAdapter->Description );
        for (UINT i = 0; i < pAdapter->AddressLength; i++) {
            if (i == (pAdapter->AddressLength - 1))
                sprintf( _mac + i*3 , "%.2X",(int)pAdapter->Address[i] );
            else
                sprintf( _mac + i*3 , "%.2X-",(int)pAdapter->Address[i] );
        }
        printf( "MAC: %s\n", _mac );
        //std::string utf8_string2 = utf_to_utf<char>( _mac );
        //exception: std::string utf8_string = to_utf<char>( _mac,"Latin1");
        //std::string utf8_string = to_utf<char>( _mac,"GB2312");
        //device.AddMember( "mac", StringRef( utf8_string.c_str() ), allocator );
        Value v_mac( rapidjson::kStringType );
        v_mac.SetString( _mac, allocator ) ;
        interface.AddMember("MAC", v_mac, allocator );
        //strcpy( ip, pAdapter->IpAddressList.IpAddress.String );
        Value v_ip( rapidjson::kStringType );
        v_ip.SetString( pAdapter->IpAddressList.IpAddress.String, allocator ) ;
        interface.AddMember("net_ip", v_ip, allocator );
        pAdapter = pAdapter->Next;
        interfaces.PushBack(interface, allocator );
    }

    if (pAdapterInfo)
        free(pAdapterInfo);

    document.AddMember("interfaces", interfaces, allocator );
    //{{"post":"host_info", hostname:"xx", datetime:"xx" },
    //{"get": "hosts_info" }}
    document.AddMember( key_request_post, StringRef( key_request_post_value_host_info ), allocator );

    document.AddMember( key_request_backup, backup, allocator );
    //document.AddMember( "MAC", "24-4B-FE-8A-20-99", allocator );

    //
    WCHAR host_name[256];
    DWORD _size;
    GetComputerName( host_name, &_size );
    fprintf( stdout, "Hostname (function: %s):  %ls\n", __FUNCTION__, host_name  );

    wxString w( host_name );
    const char* s = w.mb_str( wxConvUTF8 );
    document.AddMember( key_request_hostname, StringRef( s ), allocator );

    //.mb_str( wxConvUTF8 )
    char datetime[64];
    GetNowTime( datetime );
    s = wxString(datetime).mb_str( wxConvUTF8 );
    document.AddMember( key_request_datetime, StringRef( datetime ), allocator );

    char client_install_dir[256];
    _getcwd( client_install_dir ,256 );
    document.AddMember( key_request_host_client_install_dir, StringRef(client_install_dir), allocator );

    //
    if( IsWindows7OrGreater() )
        document.AddMember( key_request_host_system, StringRef( "Win10" ), allocator );
    else
        document.AddMember( key_request_host_system, StringRef( "Win7" ), allocator );

    document.AddMember( key_request_get, StringRef( key_request_get_value_host_info ), allocator );

    StringBuffer stringBuffer;
    Writer<StringBuffer> writer( stringBuffer );
    document.Accept(writer );

    return stringBuffer;
}

static int post_info( SOCKET sock_Client, const char* buf, int size, SOCKADDR* addr_server ){
    int err = sendto( sock_Client, buf, size,
                      0,(SOCKADDR*)&addr_server, sizeof(SOCKADDR) );
    return err;
}

/*
 * start the new thread
 */
static int init(){

}

static int query_server_status( wxWindow* parent, SOCKET sock_Client,
                                SOCKADDR_IN addr_server ){
    StringBuffer  stringBuffer = GetServerStatus(  );
    SOCKADDR_IN sock;

    int len = sizeof(sock);
    const char* s = stringBuffer.GetString();
    sendto( sock_Client, stringBuffer.GetString(), stringBuffer.GetSize(),
            0,(SOCKADDR*)&addr_server, sizeof(SOCKADDR) );
    memset( recvBuf, 0, BUFF_LEN );
    int last = recvfrom( sock_Client, recvBuf, BUFF_LEN,
                         0, (SOCKADDR*)&sock, &len );
    if(last>0){
        cout<<last<<endl;
        recvBuf[ last ] = 0;
        cout<< recvBuf <<endl;
    }
    ActionEvent event ( parent, TOKEN_UDP_MESSAGE_RESPONSE, recvBuf );
    event.Post();
    return 0;
}


WorkThread::WorkThread ( wxWindow * parent ){
    this->parent = parent;
}

void * WorkThread::Entry(){
    udp_loop( );
    return nullptr;
}

WorkThread::~WorkThread(){

}

int WorkThread::udp_loop() {
    /*
    setlocale(LC_ALL, "chinese");
    wxString s( wxT("some string") );
    wxCharBuffer buffer = s.ToUTF8();
    wxString sql;
    sql.sprintf(_T("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='%s';"), _T("preferences") );
    wxPrintf( L"%s\n", sql );

    wxString ss(u8"中文测试" );
    //wxString ss(L"中文测试" );
    //wxString ss(L"123" );
    //ss = L"123";
    const char* _s = ss.mb_str(wxConvUTF8);

    WCHAR host_name[256];
    DWORD _size;
    GetComputerName( host_name, &_size );
    wxPrintf( L"%s\n", host_name );
    wxString _host_name = host_name;
    wxPrintf( L"%s\n", _host_name );
    //const char* s = _host_name.mb_str(wxConvUTF8);
    */
    //std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
    //std::string s = convert.to_bytes(uchars);

    //string utf8_string = to_utf<char>( u8"我们","Latin1");
    //string s = u8"我们";
    //std::string utf8_string2 = utf_to_utf<char>(L"我们");
    //std::wstring wide_string = to_utf<wchar_t>(latin1_string,"Latin1");

    //test();
    SOCKET sock_Client; //客户端用于通信的Socket
    // auto recvBuf = make_shared<char>(BUFF_LEN); //发送数据的缓冲区
    //char  sendBuf[BUFF_LEN]; //接受数据的缓冲区

    sock_Client = socket( AF_INET,SOCK_DGRAM,IPPROTO_UDP );//创建客户端用于通信的Socket

    SetNonBlocking( sock_Client );
    SOCKADDR_IN addr_server;   //服务器的地址数据结构
    addr_server.sin_family = AF_INET;
    addr_server.sin_port = htons( touch_server_port );//端口号为4567
    addr_server.sin_addr.S_un.S_addr = inet_addr( touch_server_ip.c_str() );
    SOCKADDR_IN sock;
    int count = 0;
    int len = sizeof(sock);
    while( !TestDestroy() ){
        count ++;
        if( count == server_status_delay ){
            query_server_status( parent, sock_Client, addr_server );
            count = 0;
        }
        //Document document;
        StringBuffer  stringBuffer = GetResponseDocument(  );

        //wprintf( L"FORM: \n%s\n", stringBuffer.GetString() );
        //printf( "FORM: \n%s\n", stringBuffer.GetString() );

        sendto( sock_Client, stringBuffer.GetString(), stringBuffer.GetSize(),
                0,(SOCKADDR*)&addr_server, sizeof(SOCKADDR) );
        //int last=recv(sock_Client, recvBuf, strlen(recvBuf), 0);
        // (调用recv和recvfrom都可以)
        memset( recvBuf, 0, BUFF_LEN );
        int err = recvfrom( sock_Client, recvBuf, BUFF_LEN,
                             0, (SOCKADDR*)&sock, &len );
        if(err>0){
            //cout<<last<<endl;
            //recvBuf[ err ] = 0;
            //cout<< recvBuf <<endl;
            fprintf( stderr, "Response: function( %s ), line( %d ), received(%d)\n%s\n",
                    __FUNCTION__ , __LINE__, err, recvBuf );
        }
        ActionEvent event ( parent, TOKEN_UDP_MESSAGE_RESPONSE, recvBuf );
        event.Post();
        Sleep( delay * 1000 );
    }
    closesocket(sock_Client);
    WSACleanup();
    ActionEvent event ( parent, TOKEN_UDP_LOOP_END, nullptr );
    event.Post();
    return 0;
}
